home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connectio…eloper Series 2005 March / Dev.CD Mar 05.iso / What's New / Sample Code / HIObjectThreadController / HIObjectThreadController.cp < prev    next >
Encoding:
Text File  |  2005-01-10  |  20.6 KB  |  542 lines

  1. /*
  2.     File:        HIObjectThreadController.cp
  3.  
  4.     Contains:    The thread controller HIObject which handles the thread and the UI.
  5.  
  6.     Version:    1.0.1
  7.  
  8.     Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  9.                 ("Apple") in consideration of your agreement to the following terms, and your
  10.                 use, installation, modification or redistribution of this Apple software
  11.                 constitutes acceptance of these terms.  If you do not agree with these terms,
  12.                 please do not use, install, modify or redistribute this Apple software.
  13.  
  14.                 In consideration of your agreement to abide by the following terms, and subject
  15.                 to these terms, Apple grants you a personal, non-exclusive license, under Apple‚Äôs
  16.                 copyrights in this original Apple software (the "Apple Software"), to use,
  17.                 reproduce, modify and redistribute the Apple Software, with or without
  18.                 modifications, in source and/or binary forms; provided that if you redistribute
  19.                 the Apple Software in its entirety and without modifications, you must retain
  20.                 this notice and the following text and disclaimers in all such redistributions of
  21.                 the Apple Software.  Neither the name, trademarks, service marks or logos of
  22.                 Apple Computer, Inc. may be used to endorse or promote products derived from the
  23.                 Apple Software without specific prior written permission from Apple.  Except as
  24.                 expressly stated in this notice, no other rights or licenses, express or implied,
  25.                 are granted by Apple herein, including but not limited to any patent rights that
  26.                 may be infringed by your derivative works or by other works in which the Apple
  27.                 Software may be incorporated.
  28.  
  29.                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  30.                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  31.                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  32.                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  33.                 COMBINATION WITH YOUR PRODUCTS.
  34.  
  35.                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  36.                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  37.                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  38.                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  39.                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  40.                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  41.                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  
  43.     Copyright ¬© 2003 Apple Computer, Inc., All Rights Reserved
  44. */
  45.  
  46. #include "SomeTasks.h"
  47. #include "HIObjectThreadController.h"
  48.  
  49. typedef struct
  50.     {
  51.     HIObjectRef hiObject;
  52.     MPTaskID        taskID;
  53.     SetUpProc    setUpProc;
  54.     TaskProc        entryPoint;
  55.     TermProc        termProc;
  56.     void *        parameters;
  57.     HIViewRef    hiThreadPane;
  58.     } ThreadControllerData;
  59.  
  60. #if CLOSING_THE_WINDOW_WILL_STOP_ALL_THREADS
  61.  
  62. // Depending how we want the User Interface to control the threads, we may choose to have all the
  63. // threads stopped if we close the window containing the panes displaying the threads progress.
  64.  
  65. pascal OSStatus ThreadPaneIsDestroyed(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon)
  66.     {
  67.     HIObjectRef hiObject = (HIObjectRef) GetControlReference((ControlRef) inRefcon);
  68.     if (hiObject != NULL)
  69.         {
  70.         ThreadControllerData * myData = (ThreadControllerData *) HIObjectDynamicCast(hiObject, kHIObjectThreadControllerClassID);
  71.         myData->hiThreadPane = NULL;
  72.         SetControlReference((ControlRef) inRefcon, NULL);
  73.         CFRelease(hiObject);
  74.         }
  75.     return eventNotHandledErr;
  76.     }
  77.  
  78. #endif
  79.  
  80. OSStatus CreateThreadPane(int threadPaneIndex, WindowRef theWind, SInt32 knownEnd, HIViewRef * outThreadPane)
  81.     {
  82.     OSStatus status;
  83.     WindowRef windowFromNib;
  84.     HIViewRef userPane = NULL;
  85.     ControlID userPaneID = { kControlKindHIThreadPane, 100 };
  86.     
  87.     // Instantiate the window "KnownEnd" or the window "UnknownEnd".
  88.     // These name are set in InterfaceBuilder when the nib is created.
  89.     if (knownEnd >= kIndeterminateBar)
  90.         status = CreateWindowFromNib(gIBNibRef, CFSTR("KnownEnd"), &windowFromNib);
  91.     else
  92.         status = CreateWindowFromNib(gIBNibRef, CFSTR("UnknownEnd"), &windowFromNib);
  93.     require_noerr( status, CantCreateWindow );
  94.     
  95.     status = HIViewFindByID(HIViewGetRoot(windowFromNib), userPaneID, &userPane);
  96.     require_noerr( status, CantCreateWindow );
  97.  
  98.     HIViewRef contentView;
  99.     HIViewFindByID(HIViewGetRoot(theWind), kHIViewWindowContentID, &contentView);
  100.  
  101.     // Moving the pane to the appropriate location in the window depending on the
  102.     // number of panes already present.
  103.     HIViewAddSubview(contentView, userPane);
  104.     HIRect hiBounds;
  105.     HIViewGetBounds(userPane, &hiBounds);
  106.     HIViewMoveBy(userPane, 0.0, threadPaneIndex * hiBounds.size.height);
  107.     userPaneID.id = 100 + ++threadPaneIndex;
  108.     SetControlID(userPane, &userPaneID);
  109.  
  110.     // If the end is indeterminate, we need to adjust the progress bar control to
  111.     // reflect that.
  112.     if (knownEnd == kIndeterminateBar)
  113.         {
  114.         ControlID progressBarID = { 'Prgb', 100 };
  115.         HIViewRef progressBar;
  116.         if ((HIViewFindByID(userPane, progressBarID, &progressBar) == noErr) && (progressBar != NULL))
  117.             {
  118.             Boolean indeterminate = true;
  119.             SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(indeterminate), &indeterminate);
  120.             }
  121.         }
  122.  
  123.     HIViewSetVisible(userPane, true);
  124.  
  125.     // We don't need the window anymore.
  126.     DisposeWindow(windowFromNib);
  127.  
  128. #if CLOSING_THE_WINDOW_WILL_STOP_ALL_THREADS
  129.     {
  130.     EventTypeSpec eventType = {kEventClassHIObject, kEventHIObjectDestruct};
  131.     InstallEventHandler(GetControlEventTarget(userPane), ThreadPaneIsDestroyed, 1, &eventType, (void *)userPane, NULL);
  132.     }
  133. #endif
  134.  
  135. CantCreateWindow:
  136.     *outThreadPane = userPane;
  137.     return status;
  138.     }
  139.  
  140. // Determining how many panes are present by iterating over the children of the
  141. // content view and checking their signature.
  142. int HowManyThreadPanesInWindow(WindowRef theWind)
  143.     {
  144.     HIViewRef contentView;
  145.     HIViewFindByID(HIViewGetRoot(theWind), kHIViewWindowContentID, &contentView);
  146.     
  147.     int threadPanesInWindow = 0;
  148.     HIViewRef pane = HIViewGetFirstSubview(contentView);
  149.     
  150.     while (pane != NULL)
  151.         {
  152.         ControlID theID;
  153.         GetControlID(pane, &theID);
  154.         if (theID.signature == kControlKindHIThreadPane) threadPanesInWindow++;
  155.         pane = HIViewGetNextView(pane);
  156.         }
  157.     
  158.     return threadPanesInWindow;
  159.     }
  160.  
  161. // If the thread is running then enable the "Stop" button
  162. // If not then enable the "Start" button
  163. // and disable the other button.
  164. // We also update the chasing arrows and indeterminate progress bar if we have them.
  165. void UpdateButtons(HIViewRef hiThreadPane, Boolean starting)
  166.     {
  167.     ControlID startButtonID = { 'Srtb', 100 };
  168.     HIViewRef startButton;
  169.     HIViewFindByID(hiThreadPane, startButtonID, &startButton);
  170.     if (starting) DisableControl(startButton); else EnableControl(startButton);
  171.     ControlID stopButtonID = { 'Stpb', 100 };
  172.     HIViewRef stopButton;
  173.     HIViewFindByID(hiThreadPane, stopButtonID, &stopButton);
  174.     if (starting) EnableControl(stopButton); else DisableControl(stopButton);
  175.     
  176.     // Is the thread done?
  177.     if (!starting)
  178.         {
  179.         ControlID arrowsID = { 'Prgw', 100 };
  180.         HIViewRef arrows = NULL;
  181.         if ((HIViewFindByID(hiThreadPane, arrowsID, &arrows) == noErr) && (arrows != NULL))
  182.             {
  183.             // If we have chasing arrows then we hide them
  184.             HideControl(arrows);
  185.             }
  186.         
  187.         ControlID progressBarID = { 'Prgb', 100 };
  188.         HIViewRef progressBar = NULL;
  189.         if ((HIViewFindByID(hiThreadPane, progressBarID, &progressBar) == noErr) && (progressBar != NULL))
  190.             {
  191.             Boolean indeterminate;
  192.             GetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(indeterminate), &indeterminate, NULL);
  193.             if (indeterminate)
  194.                 {
  195.                 // If we have an indeterminate progress bar then we stop the animation
  196.                 Boolean animating = false;
  197.                 SetControlData(progressBar, kControlEntireControl, kControlProgressBarAnimatingTag, sizeof(animating), &animating);
  198.                 }
  199.             }
  200.         }
  201.     }
  202.  
  203. // After receiving the kEventUpdateThreadUI event, we will have to update the progress bar
  204. // and the static text containing the current value of pi being calculated.
  205. // All the relevant information is contained in the task params.
  206. void UpdateUI(ThreadControllerData * myData)
  207.     {
  208.     GeneralTaskWorkParamsPtr params = (GeneralTaskWorkParamsPtr) myData->parameters;
  209.  
  210.     ControlID progressBarID = { 'Prgb', 100 };
  211.     HIViewRef progressBar;
  212.     HIViewFindByID(myData->hiThreadPane, progressBarID, &progressBar);
  213.     SetControl32BitValue(progressBar, (SInt32)(10000.0 * params->iterator / kSTEndIteration));
  214.  
  215.     ControlID staticTextID = { 'Sttt', 100 };
  216.     HIViewRef staticText;
  217.     HIViewFindByID(myData->hiThreadPane, staticTextID, &staticText);
  218.  
  219.     CFStringRef theCFString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%.50f"), params->result);
  220.     SetControlData(staticText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), &theCFString);
  221.     HIViewSetNeedsDisplay(staticText, true);
  222.     CFRelease(theCFString);
  223.     }
  224.  
  225. // Starting a thread by calling its setup routine, the Multiprocessing Services API
  226. // to actually start it, and updating the User Interface.
  227. OSStatus HIObjectThreadControllerStartThread(HIObjectRef threadController)
  228.     {
  229.     OSStatus status = noErr;
  230.     ThreadControllerData * myData = (ThreadControllerData *) HIObjectDynamicCast(threadController, kHIObjectThreadControllerClassID);
  231.     EventTargetRef theTarget = HIObjectGetEventTarget(myData->hiObject);
  232.     myData->parameters = myData->setUpProc();
  233.     ((GeneralTaskWorkParamsPtr)myData->parameters)->threadControllerTarget = theTarget;
  234.     status = MPCreateTask(myData->entryPoint, myData->parameters, 0, NULL, NULL, NULL, 0, &myData->taskID);
  235.  
  236.     // If we have chasing arrows then we make sure they are visible
  237.     ControlID arrowsID = { 'Prgw', 100 };
  238.     HIViewRef arrows = NULL;
  239.     if ((HIViewFindByID(myData->hiThreadPane, arrowsID, &arrows) == noErr) && (arrows != NULL))
  240.         ShowControl(arrows);
  241.  
  242.     // If we have an indeterminate progress bar then we start the animation
  243.     ControlID progressBarID = { 'Prgb', 100 };
  244.     HIViewRef progressBar = NULL;
  245.     if ((HIViewFindByID(myData->hiThreadPane, progressBarID, &progressBar) == noErr) && (progressBar != NULL))
  246.         {
  247.         Boolean indeterminate;
  248.         GetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(indeterminate), &indeterminate, NULL);
  249.         if (indeterminate)
  250.             {
  251.             Boolean animating = true;
  252.             SetControlData(progressBar, kControlEntireControl, kControlProgressBarAnimatingTag, sizeof(animating), &animating);
  253.             HIViewSetNeedsDisplay(progressBar, true);
  254.             }
  255.         }
  256.  
  257.     UpdateUI(myData);
  258.     UpdateButtons(myData->hiThreadPane, true);
  259.     return status;
  260.     }
  261.  
  262. // Terminating a thread by calling its cleanup and updating the User Interface.
  263. // When we reach this function, the thread has already been terminated by the Multiprocessing Services.
  264. void HIObjectThreadControllerTermThread(HIObjectRef threadController)
  265.     {
  266.     ThreadControllerData * myData = (ThreadControllerData *) HIObjectDynamicCast(threadController, kHIObjectThreadControllerClassID);
  267.     if (myData->taskID != NULL)
  268.         {
  269.         myData->taskID = NULL;
  270.         UpdateUI(myData);
  271.         UpdateButtons(myData->hiThreadPane, false);
  272.         myData->termProc(myData->parameters);
  273.         }
  274.     }
  275.  
  276. // Stopping a thread by calling the Multiprocessing Services API if need be.
  277. OSStatus HIObjectThreadControllerStopThread(HIObjectRef threadController)
  278.     {
  279.     OSStatus status = noErr;
  280.     ThreadControllerData * myData = (ThreadControllerData *) HIObjectDynamicCast(threadController, kHIObjectThreadControllerClassID);
  281.     if (myData->taskID != NULL)
  282.         {
  283.         status = MPTerminateTask(myData->taskID, kMPTaskStoppedErr);
  284.         HIObjectThreadControllerTermThread(threadController);
  285.         }
  286.     return status;
  287.     }
  288.  
  289. // Handling the clicks on the "Start" and "Stop" buttons
  290. pascal OSStatus WindowCommandProcess(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
  291.     {
  292.     HICommandExtended aCommand;
  293.     OSStatus status = noErr;
  294.  
  295.     GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(aCommand), NULL, &aCommand);
  296.       
  297.     switch (aCommand.commandID)
  298.         {
  299.         case 'Srtb':
  300.             HIObjectThreadControllerStartThread((HIObjectRef) GetControlReference(HIViewGetSuperview(aCommand.source.control)));
  301.             break;
  302.         case 'Stpb':
  303.             HIObjectThreadControllerStopThread((HIObjectRef) GetControlReference(HIViewGetSuperview(aCommand.source.control)));
  304.             break;
  305.         default:
  306.             status = eventNotHandledErr;
  307.             break;
  308.         }
  309.     return status;
  310.     }
  311.  
  312. // Creates a new window and sets its title
  313. void NewWindowForThreads(void)
  314.     {
  315.     static int windNumber = 1;
  316.     EventTypeSpec eventTypeCP = {kEventClassCommand, kEventCommandProcess};
  317.     WindowRef theWind;
  318.     CFStringRef windTitle = NULL;
  319.     Rect bounds = {40, 10, 680, 510};
  320.     OSStatus status = CreateNewWindow(
  321.                                     kDocumentWindowClass,
  322.                                     kWindowStandardFloatingAttributes |
  323.                                     kWindowStandardHandlerAttribute |
  324.                                     kWindowCompositingAttribute,
  325.                                     &bounds, &theWind);
  326.     require_noerr(status, exitNewWindow);
  327.     require(theWind != NULL, exitNewWindow);
  328.  
  329.     status = RepositionWindow(theWind, NULL, kWindowCascadeOnMainScreen);
  330.     require_noerr(status, exitNewWindow);
  331.     
  332.     windTitle = CFStringCreateWithFormat(NULL, NULL, CFSTR("Threads Window #%d"), windNumber++);
  333.     SetWindowTitleWithCFString(theWind, windTitle);
  334.     CFRelease(windTitle);
  335.  
  336.     // Let's react to User's commands.
  337.     InstallEventHandler(GetWindowEventTarget(theWind), WindowCommandProcess, 1, &eventTypeCP, NULL, NULL);
  338.     
  339.     ShowWindow(theWind);
  340.  
  341. exitNewWindow:
  342.     return;
  343.     }
  344.  
  345. // Checks for a front window, creates one if needed
  346. // then checks if one more pane can be added in the window
  347. OSStatus NewThreadPaneInFrontWindow(SInt32 knownEnd, HIViewRef * outThreadPane)
  348.     {
  349.     WindowRef theFrontWindow = GetFrontWindowOfClass(kDocumentWindowClass, true);
  350.     if (theFrontWindow == NULL)
  351.         {
  352.         NewWindowForThreads();
  353.         theFrontWindow = GetFrontWindowOfClass(kDocumentWindowClass, true);
  354.         }
  355.  
  356.     int threadPanesInWindow = HowManyThreadPanesInWindow(theFrontWindow);
  357.     
  358.     if (threadPanesInWindow > 8)
  359.         {
  360. #if WITHALERT
  361.         StandardAlert(kAlertStopAlert, "\pNo more threads allowed in this window.", "\pA new window will be created.", NULL, NULL);
  362. #endif
  363.         NewWindowForThreads();
  364.         theFrontWindow = GetFrontWindowOfClass(kDocumentWindowClass, true);
  365.         threadPanesInWindow = 0;
  366.         }
  367.  
  368.     // To illustrate the different choices of User Interface, we artificially
  369.     // (one in five) setup a thread as indeterminate (either bar or chasing arrows).
  370.     SInt32 ending = knownEnd;
  371.     if ((threadPanesInWindow % 5) == 3) ending = ((threadPanesInWindow % 2) == 0)?kIndeterminateBar:kIndeterminateChasing;
  372.  
  373.     return CreateThreadPane(threadPanesInWindow, theFrontWindow, ending, outThreadPane);
  374.     }
  375.  
  376. pascal OSStatus ThreadControllerHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon)
  377.     {
  378.     OSStatus    status = eventNotHandledErr;
  379.     ThreadControllerData* myData = (ThreadControllerData*) inRefcon;
  380.  
  381.     switch (GetEventClass(inEvent))
  382.         {
  383.         case kEventClassHIObject:
  384.             switch (GetEventKind(inEvent))
  385.                 {
  386.                 case kEventHIObjectConstruct:
  387.                     {
  388.                     myData = (ThreadControllerData*) calloc(1, sizeof(ThreadControllerData));
  389.                     require_string((myData != NULL), exitHandler, "ThreadControllerHandler--kEventHIObjectConstruct--calloc ");
  390.  
  391.                     // get our superclass instance
  392.                     HIObjectRef hiObject;
  393.                     status = GetEventParameter(inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof(hiObject), NULL, &hiObject);
  394.                     require_noerr_string(status, exitHandler, "ThreadControllerHandler--kEventHIObjectConstruct--GetEventParameter ");
  395.                     myData->hiObject = hiObject;
  396.                     myData->taskID = NULL;
  397.                     myData->hiThreadPane = NULL;
  398.                     myData->parameters = NULL;
  399.  
  400.                     // store our instance data into the event
  401.                     status = SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof(myData), &myData);
  402.                     require_noerr_string(status, exitHandler, "ThreadControllerHandler--kEventHIObjectConstruct--SetEventParameter ");
  403.                     }
  404.                     break;
  405.  
  406.                 case kEventHIObjectInitialize:
  407.                     {
  408.                     // always begin kEventHIObjectInitialize by calling through to the previous handler
  409.                     status = CallNextEventHandler(inCaller, inEvent);
  410.                     // if that succeeded, do our own initialization
  411.                     if (status == noErr)
  412.                         {
  413.                         CFStringRef theLabel;
  414.                         GetEventParameter(inEvent, 'Tclb', typeCFStringRef, NULL, sizeof(theLabel), NULL, &theLabel);
  415.                         GetEventParameter(inEvent, 'Tcsu', typeVoidPtr, NULL, sizeof(myData->setUpProc), NULL, &myData->setUpProc);
  416.                         GetEventParameter(inEvent, 'Tcep', typeVoidPtr, NULL, sizeof(myData->entryPoint), NULL, &myData->entryPoint);
  417.                         GetEventParameter(inEvent, 'Tctp', typeVoidPtr, NULL, sizeof(myData->termProc), NULL, &myData->termProc);
  418.                         SInt32 knownEnd;
  419.                         GetEventParameter(inEvent, 'Tcke', typeSInt32, NULL, sizeof(knownEnd), NULL, &knownEnd);
  420.                         status = NewThreadPaneInFrontWindow(knownEnd, &myData->hiThreadPane);
  421.                         require_noerr_string(status, exitHandler, "ThreadControllerHandler--kEventHIObjectInitialize--NewThreadPaneInFrontWindow ");
  422.                         
  423.                         // associating the pane and the thread controller
  424.                         SetControlReference(myData->hiThreadPane, (SInt32) myData->hiObject);
  425.                         
  426.                         // setting the label
  427.                         ControlID labelID = { 'Sttt', 101 };
  428.                         HIViewRef label;
  429.                         HIViewFindByID(myData->hiThreadPane, labelID, &label);
  430.                         SetControlData(label, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), &theLabel);
  431.                         
  432.                         HIObjectThreadControllerStartThread(myData->hiObject);
  433.                         }
  434.                     }
  435.                     break;
  436.  
  437.                 case kEventHIObjectDestruct:
  438.                     {
  439.                     HIObjectThreadControllerStopThread(myData->hiObject);
  440.                     if (myData->hiThreadPane != NULL)
  441.                         {
  442.                         DisposeControl(myData->hiThreadPane);
  443.                         myData->hiThreadPane = NULL;
  444.                         }
  445.                     free(myData);
  446.                     }
  447.                     break;
  448.                 
  449.                 default:
  450.                     break;
  451.                 }
  452.             break;
  453.             
  454.         case kEventClassHIObjectThreadController:
  455.             switch (GetEventKind(inEvent))
  456.                 {
  457.                 case kEventUpdateThreadUI:
  458.                     UpdateUI(myData);
  459.                     break;
  460.                 
  461.                 case kEventTerminateThread:
  462.                     HIObjectThreadControllerTermThread(myData->hiObject);
  463.                     break;
  464.                 
  465.                 default:
  466.                     break;
  467.                 }
  468.             break;
  469.         
  470.         default:
  471.             break;
  472.         }
  473.  
  474. exitHandler:
  475.     return status;
  476.     }
  477.  
  478. // Registering our class and setting the handlers
  479. CFStringRef GetThreadControllerClass()
  480.     {
  481.     static HIObjectClassRef    theClass;
  482.     
  483.     if (theClass == NULL)
  484.         {
  485.         static EventTypeSpec kFactoryEvents[] =
  486.             {
  487.                 { kEventClassHIObject, kEventHIObjectConstruct },
  488.                 { kEventClassHIObject, kEventHIObjectDestruct },
  489.                 { kEventClassHIObject, kEventHIObjectInitialize },
  490.                 { kEventClassHIObjectThreadController, kEventUpdateThreadUI },
  491.                 { kEventClassHIObjectThreadController, kEventTerminateThread }
  492.             };
  493.         
  494.         HIObjectRegisterSubclass(kHIObjectThreadControllerClassID, NULL, 0, ThreadControllerHandler,
  495.                                   GetEventTypeCount(kFactoryEvents), kFactoryEvents, NULL, &theClass);
  496.         }
  497.     
  498.     return kHIObjectThreadControllerClassID;
  499.     }
  500.  
  501. // Creating our thread controller object
  502. // This is mostly setting up the Initialization event with the parameters
  503. extern OSStatus
  504. HIObjectThreadControllerCreate(
  505.     CFStringRef inLabel,
  506.     SetUpProc inSetUpProc,
  507.     TaskProc inEntryPoint,
  508.     TermProc inTermProc,
  509.     SInt32 inKnownEnd,
  510.     HIViewRef * outHIThreadPane,
  511.     HIObjectRef * outHIObjectThreadController)
  512.     {
  513.     OSStatus status = noErr;
  514.     EventRef theInitializeEvent = NULL;
  515.     ThreadControllerData * myData = NULL;
  516.     HIObjectRef hiObject;
  517.     
  518.     status = CreateEvent(NULL, kEventClassHIObject, kEventHIObjectInitialize, GetCurrentEventTime(), kEventAttributeUserEvent, &theInitializeEvent);
  519.     require_noerr_string(status, exitCreate, "HIThreadControllerCreate--CreateEvent ");
  520.     status = SetEventParameter(theInitializeEvent, 'Tclb', typeCFStringRef, sizeof(inLabel), &inLabel);
  521.     require_noerr_string(status, exitCreate, "HIThreadControllerCreate--SetEventParameter TCLB ");
  522.     status = SetEventParameter(theInitializeEvent, 'Tcsu', typeVoidPtr, sizeof(inSetUpProc), &inSetUpProc);
  523.     require_noerr_string(status, exitCreate, "HIThreadControllerCreate--SetEventParameter TCSU ");
  524.     status = SetEventParameter(theInitializeEvent, 'Tcep', typeVoidPtr, sizeof(inEntryPoint), &inEntryPoint);
  525.     require_noerr_string(status, exitCreate, "HIThreadControllerCreate--SetEventParameter TCEP ");
  526.     status = SetEventParameter(theInitializeEvent, 'Tctp', typeVoidPtr, sizeof(inTermProc), &inTermProc);
  527.     require_noerr_string(status, exitCreate, "HIThreadControllerCreate--SetEventParameter TCTP ");
  528.     status = SetEventParameter(theInitializeEvent, 'Tcke', typeSInt32, sizeof(inKnownEnd), &inKnownEnd);
  529.     require_noerr_string(status, exitCreate, "HIThreadControllerCreate--SetEventParameter TCKE ");
  530.  
  531.     status = HIObjectCreate(GetThreadControllerClass(), theInitializeEvent, &hiObject);
  532.     require_noerr_string(status, exitCreate, "HIThreadControllerCreate--HIObjectCreate ");
  533.     
  534.     myData = (ThreadControllerData *) HIObjectDynamicCast(hiObject, kHIObjectThreadControllerClassID);
  535.  
  536. exitCreate:
  537.     if (theInitializeEvent != NULL) ReleaseEvent(theInitializeEvent);
  538.     if (outHIThreadPane != NULL) *outHIThreadPane = myData->hiThreadPane;
  539.     if (outHIObjectThreadController != NULL) *outHIObjectThreadController = hiObject;
  540.     return status;
  541.     }
  542.